07 Docker 基础 - 数据卷挂载
容器是隔离环境,容器内程序的文件、配置、运行时产生的容器都在容器内部,我们要读写容器内的文件非常不方便。大家思考几个问题:
- 如果要升级 MySQL 版本,需要销毁旧容器,那么数据岂不是跟着被销毁了?
- MySQL、Nginx 容器运行后,如果我要修改其中的某些配置该怎么办?
- 我想要让 Nginx 代理我的静态资源怎么办?
因此,容器提供程序的运行环境,但是程序运行产生的数据、程序运行依赖的配置都应该与容器解耦。
什么是数据卷
数据卷(volume) 是一个虚拟目录,是 容器内目录 与 宿主机目录 之间映射的桥梁。
官方解释
Volumes are the preferred mechanism for persisting data generated by and used by Docker containers. While bind mounts are dependent on the directory structure and OS of the host machine, volumes are completely managed by Docker. Volumes have several advantages over bind mounts: 卷是 Docker 容器生成和使用数据的首选持久化机制。绑定挂载依赖于主机的目录结构和操作系统,而卷则完全由 Docker 管理。与绑定挂载相比,卷有几个优点:
- Volumes are easier to back up or migrate than bind mounts. 卷比绑定挂载更容易备份或迁移。
- You can manage volumes using Docker CLI commands or the Docker API. 您可以使用 Docker CLI 命令或 Docker API 管理卷。
- Volumes work on both Linux and Windows containers. 卷可在 Linux 和 Windows 容器上运行。
- Volumes can be more safely shared among multiple containers. 多个容器可以更安全地共享卷。
- Volume drivers let you store volumes on remote hosts or cloud providers, encrypt the contents of volumes, or add other functionality. 卷驱动程序可以让你在远程主机或云提供商上存储卷,加密卷的内容,或添加其他功能。
- New volumes can have their content pre-populated by a container. 新卷的内容可由容器预先填充。
- Volumes on Docker Desktop have much higher performance than bind mounts from Mac and Windows hosts. 与 Mac 和 Windows 主机上的绑定挂载相比,Docker Desktop 上的卷具有更高的性能。
In addition, volumes are often a better choice than persisting data in a container's writable layer, because a volume doesn't increase the size of the containers using it, and the volume's contents exist outside the lifecycle of a given container. 此外,与在容器的可写层中持久化数据相比,卷通常是更好的选择,因为卷不会增加使用它的容器的大小,而且卷的内容存在于给定容器的生命周期之外。
以 Nginx 为例,我们知道 Nginx 中有两个关键的目录:
html:放置一些静态资源conf:放置配置文件
如果我们要让 Nginx 代理我们的静态资源,最好是放到 html 目录;如果我们要修改 Nginx 的配置,最好是找到 conf 下的 nginx.conf 文件。
但遗憾的是,容器运行的 Nginx 所有的文件都在容器内部。所以我们必须利用数据卷将两个目录与宿主机目录关联,方便我们操作。

在上图中:
- 我们创建了两个数据卷:
conf、html - Nginx 容器内部的
conf目录和html目录分别与两个数据卷关联。 - 而数据卷 conf 和 html 分别指向了宿主机的
/var/lib/docker/volumes/conf/_data目录和/var/lib/docker/volumes/html/_data目录
这样以来,容器内的 conf 和 html 目录就 与宿主机的 conf 和 html 目录关联起来,我们称为挂载。此时,我们操作宿主机的 /var/lib/docker/volumes/html/_data 就是在操作容器内的 /usr/share/nginx/html/_data 目录。只要我们将静态资源放入宿主机对应目录,就可以被 Nginx 代理了。
TIP
/var/lib/docker/volumes这个目录就是默认的存放所有容器数据卷的目录,其下再根据数据卷名称创建新目录,格式为/数据卷名/_data。- 为什么不让容器目录直接指向宿主机目录呢?
- 因为直接指向宿主机目录就与宿主机强耦合了,如果切换了环境,宿主机目录就可能发生改变了。由于容器一旦创建,目录挂载就无法修改,这样容器就无法正常工作了。
- 但是容器指向数据卷,一个逻辑名称,而数据卷再指向宿主机目录,就不存在强耦合。如果宿主机目录发生改变,只要改变数据卷与宿主机目录之间的映射关系即可。
- 不过,我们通过由于数据卷目录比较深,不好寻找,通常我们也 允许让容器直接与宿主机目录挂载而不使用数据卷,具体参考 08.Docker 基础 - 本地目录挂载。
数据卷命令
docker volume command
❯ docker volume --help
Usage: docker volume COMMAND
Manage volumes
Commands:
create Create a volume
inspect Display detailed information on one or more volumes
ls List volumes
prune Remove unused local volumes
rm Remove one or more volumes
Run 'docker volume COMMAND --help' for more information on a command.❯ tldr docker volume
Manage Docker volumes.
More information: <https://docs.docker.com/engine/reference/commandline/volume/>.
Create a volume:
docker volume create volume_name
Create a volume with a specific label:
docker volume create --label label volume_name
Create a `tmpfs` volume a size of 100 MiB and an uid of 1000:
docker volume create --opt type=tmpfs --opt device=tmpfs --opt o=size=100m,uid=1000 volume_name
List all volumes:
docker volume ls
Remove a volume:
docker volume rm volume_name
Display information about a volume:
docker volume inspect volume_name
Remove all unused local volumes:
docker volume prune
Display help for a subcommand:
docker volume subcommand --help常见命令
| 命令 | 说明 | 文档地址 |
|---|---|---|
docker volume create | 创建数据卷 | docker volume create |
docker volume ls | 查看所有数据卷 | docker volume ls |
docker volume rm | 删除指定数据卷 | docker volume rm |
docker volume inspect | 查看某个数据卷的详情 | docker volume inspect |
docker volume prune | 清除数据卷 | docker volume prune |
注意
容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。而且创建容器的过程中,数据卷会自动创建。
volume 命令演示
命名加密卷
演示一下利用 Nginx 容器部署静态资源:nginx 的 html 命名数据卷。
需求
- 创建 Nginx 容器,修改 nginx 容器内的
html目录下的index.html文件内容 - 将静态资源部署到 nginx 的
html目录
提示
- 在执行
docker run命令时,使用-v 数据卷:容器内目录可以完成数据卷挂载 - 当创建容器时,如果挂载了数据卷且数据卷不存在,会自动创建数据卷
首先创建容器并指定数据卷,注意通过
-v参数来指定数据卷。bash❯ docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx 578acb154839: Pull complete e398db710407: Pull complete 85c41ebe6d66: Pull complete 7170a263b582: Pull complete 8f28d06e2e2e: Pull complete 6f837de2f887: Pull complete c1dfc7e1671e: Pull complete Digest: sha256:86e53c4c16a6a276b204b0fd3a8143d86547c967dc8258b3d47c3a21bb68d3c6 Status: Downloaded newer image for nginx:latest a415c4c37209f030752326a41ddc5485ed83b498087e6082f0121de3a3bbbb52查看数据卷。
bash❯ docker volume ls DRIVER VOLUME NAME local 0cdc74b1d00927c0e628ca8d6600316fc1c4c90b15eec972b75a8ac008661819 local html查看数据卷详情。
bash❯ docker volume inspect html [ { "CreatedAt": "2023-11-05T09:32:30+08:00", "Driver": "local", "Labels": null, "Mountpoint": "/var/lib/docker/volumes/html/_data", "Name": "html", "Options": null, "Scope": "local" } ]查看
/var/lib/docker/volumes/html/_data目录。bash❯ la .rw-r--r-- ubuntu ubuntu 497B 2 weeks ago 50x.html .rw-r--r-- ubuntu ubuntu 19B an hour ago index.html进入该目录,并随意修改 index.html 内容。
bash❯ curl http://150.158.99.53:80 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> ❯ vim index.html打开页面查看效果。
bash❯ cat -n index.html 1 Welcome to Docker! ❯ curl http://150.158.99.53:80 Welcome to Docker!进入容器内部,查看
/usr/share/nginx/html目录内的文件是否变化。bash❯ dex nginx cat /usr/share/nginx/html/index.html # dex is alias of 'docker exec -it' Welcome to Docker!
匿名加密卷
演示一下 MySQL 的匿名数据卷。
查看 MySQL 容器详细信息。
bash❯ dkin mysql # dkin is alias of 'docker inspect' ... 输出内容太多,省略 ❯ dkin mysql | jq '.[0].Config.Volumes' # 使用 jq 查看容器配置的数据卷 { "/var/lib/mysql": {} } ❯ dkin mysql | jq '.[0].Mounts' # 使用 jq 查看容器挂载的数据卷 [ { "Type": "volume", "Name": "9c23b8c7e4d34c0458f255c74c577a57cc97697f9b29619324458cbd9bd417d6", "Source": "/var/lib/docker/volumes/9c23b8c7e4d34c0458f255c74c577a57cc97697f9b29619324458cbd9bd417d6/_data", "Destination": "/var/lib/mysql", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ]使用 jq 查看容器配置的数据卷。
dkin mysql | jq '.[0].Config.Volumesjson{ "/var/lib/mysql": {} }可以发现这个容器声明了一个本地目录,需要挂载数据卷,但是数据卷未定义。这就是匿名卷。
使用 jq 查看容器挂载的数据卷。
dkin mysql | jq '.[0].Mounts'json[ { "Type": "volume", "Name": "9c23b8c7e4d34c0458f255c74c577a57cc97697f9b29619324458cbd9bd417d6", "Source": "/var/lib/docker/volumes/9c23b8c7e4d34c0458f255c74c577a57cc97697f9b29619324458cbd9bd417d6/_data", "Destination": "/var/lib/mysql", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ]可以发现,其中有几个关键属性:
- Name:数据卷名称。由于定义容器未设置容器名,这里的就是匿名卷自动生成的名字,一串 hash 值。
- Source:宿主机目录
- Destination : 容器内的目录
上述配置是将容器内的
/var/lib/mysql这个目录,与数据卷9c23b8c7e4d34c0458f255c74c577a57cc97697f9b29619324458cbd9bd417d6挂载。于是在宿主机中就有了/var/lib/docker/volumes/9c23b8c7e4d34c0458f255c74c577a57cc97697f9b29619324458cbd9bd417d6/_data这个目录。这就是匿名数据卷对应的目录,其使用方式与普通数据卷没有差别。查看该目录下的 MySQL 的 data 文件。
bash❯ lsa /var/lib/docker/volumes/9c23b8c7e4d34c0458f255c74c577a57cc97697f9b29619324458cbd9bd417d6/_data total 99M drwxrwxrwt 7 lxd ubuntu 4.0K Nov 5 11:00 . drwx-----x 3 root root 4.0K Nov 5 10:59 .. -rw-r----- 1 lxd docker 56 Nov 5 10:59 auto.cnf -rw-r----- 1 lxd docker 2.9M Nov 5 11:00 binlog.000001 -rw-r----- 1 lxd docker 157 Nov 5 11:00 binlog.000002 -rw-r----- 1 lxd docker 32 Nov 5 11:00 binlog.index -rw------- 1 lxd docker 1.7K Nov 5 11:00 ca-key.pem -rw-r--r-- 1 lxd docker 1.1K Nov 5 11:00 ca.pem -rw-r--r-- 1 lxd docker 1.1K Nov 5 11:00 client-cert.pem -rw------- 1 lxd docker 1.7K Nov 5 11:00 client-key.pem -rw-r----- 1 lxd docker 192K Nov 5 11:02 '#ib_16384_0.dblwr' -rw-r----- 1 lxd docker 8.2M Nov 5 10:59 '#ib_16384_1.dblwr' -rw-r----- 1 lxd docker 5.6K Nov 5 11:00 ib_buffer_pool -rw-r----- 1 lxd docker 12M Nov 5 11:00 ibdata1 -rw-r----- 1 lxd docker 12M Nov 5 11:00 ibtmp1 drwxr-x--- 2 lxd docker 4.0K Nov 5 11:00 '#innodb_redo' drwxr-x--- 2 lxd docker 4.0K Nov 5 11:00 '#innodb_temp' drwxr-x--- 2 lxd docker 4.0K Nov 5 11:00 mysql -rw-r----- 1 lxd docker 31M Nov 5 11:00 mysql.ibd lrwxrwxrwx 1 lxd docker 27 Nov 5 11:00 mysql.sock -> /var/run/mysqld/mysqld.sock drwxr-x--- 2 lxd docker 4.0K Nov 5 11:00 performance_schema -rw------- 1 lxd docker 1.7K Nov 5 11:00 private_key.pem -rw-r--r-- 1 lxd docker 452 Nov 5 11:00 public_key.pem -rw-r--r-- 1 lxd docker 1.1K Nov 5 11:00 server-cert.pem -rw------- 1 lxd docker 1.7K Nov 5 11:00 server-key.pem drwxr-x--- 2 lxd docker 4.0K Nov 5 11:00 sys -rw-r----- 1 lxd docker 16M Nov 5 11:02 undo_001 -rw-r----- 1 lxd docker 16M Nov 5 11:02 undo_002
注意
每一个不同的镜像,将来创建容器后内部有哪些目录可以挂载,可以参考 DockerHub 对应镜像的页面。
总结
- 什么是数据卷?
- 数据卷是一个虚拟目录,它将宿主机目录映射到容器内目录,方便我们操作容器内文件,或者方便迁移容器产生的数据
- 如何挂载数据卷?
- 在创建容器时,利用
-v 数据卷名:容器内目录完成挂载 - 容器创建时,如果发现挂载的数据卷不存在时,会自动创建
- 在创建容器时,利用
- 数据卷的常见命令有哪些?
docker volume ls: 查看数据卷docker volume rm: 删除数据卷docker volume inspect: 查看数据卷详情docker volume prune: 删除未使用的数据卷